//
//  Freebase.m
//  Freebase
//
//  Created by Alec Flett on 7/26/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import <JSON/JSON.h>
#import "Freebase.h"
#import "FreebaseQuerier.h"
#import "ImageAttacher.h"

@implementation FreebaseSession
@synthesize hostname;

////////////////////////////////////////////////////////////////////////////////
// class public

+ (FreebaseSession*)session {
    static FreebaseSession* session = nil;
    if (!session) {
        session = [[FreebaseSession alloc] init];
    }
    return session;
}


- (FreebaseSession*)init {
    if ((self = [super init])) {
        if (!hostname)
            self.hostname = @"api.freebase.com";
        _outstandingRequests = [[NSMutableArray alloc] init];
        _sessionDelegates = [[NSMutableArray alloc] init];
    }
    
    return self;
}

- (FreebaseSession*)initWithHostname: (NSString*) hname
{
    NSLog(@"FreebaseSession initWithHostname: %@", hname);
    self.hostname = hname;
    return [self init];
}

- (void)dealloc
{
    [super dealloc];
    self.hostname = nil;
}

-(void)addSessionDelegate:(id<FreebaseSessionDelegate>)delegate
{
    [_sessionDelegates addObject:delegate];
}

-(void)removeSessionDelegate:(id<FreebaseSessionDelegate>)delegate
{
    [_sessionDelegates removeObject:delegate];
}

// private, only called by helper classes
-(void)beginRequest:(id<FreebaseRequest>)request
{
    [_outstandingRequests addObject:request];
    
    NSLog(@"Begin request with %d outstanding", [_outstandingRequests count]);
    if ([_outstandingRequests count] == 1) {
        for (id<FreebaseSessionDelegate> delegate in _sessionDelegates)
            [delegate beginActivity:self];            
    }
}
-(void)endRequest:(id<FreebaseRequest>)request
{
    [_outstandingRequests removeObject:request];
    NSLog(@"End request with %d outstanding", [_outstandingRequests count]);
    if ([_outstandingRequests count] == 0) {
        for (id<FreebaseSessionDelegate> delegate in _sessionDelegates)
            [delegate endActivity:self];
    }
}



- (id <FreebaseRequest>)login:(NSString*)user_id
                     password:(NSString*)password
                     delegate:(id)delegate
            didFinishSelector:(SEL)finishedSelector
{
    FreebaseQuerier* querier = [[FreebaseQuerier alloc] initWithDelegate:delegate 
                                                            respSelector:finishedSelector
                                                               queryName:nil 
                                                                 session:self];
    querier.isNamedResponse = NO;
    querier.isResultResponse = NO;
    [querier requestUrl:@"/api/account/login" postForm:
     [NSDictionary dictionaryWithObjectsAndKeys:
      user_id, @"username",
      password, @"password",
     nil]];
    
    return querier;
}

- (id <FreebaseRequest>)mqlread:(id)query 
                name:(NSString*)name 
            delegate:(id) delegate
didFinishSelector:(SEL)finishedSelector
{
    return [self mqlread:query name:name delegate:delegate didFinishSelector:finishedSelector cursor:nil];
}

- (id <FreebaseRequest>)mqlread:(id)query name:(NSString*)name 
            delegate:(id) delegate
       didFinishSelector:(SEL)finishedSelector
           cursor:(id)cursor
{
    NSDictionary* options=nil;

    if (cursor)
        options = [NSDictionary dictionaryWithObjectsAndKeys:
                   cursor, @"cursor",
                   [NSNumber numberWithBool:NO], @"escape", nil];    
    else
        options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]
                                              forKey:@"escape"];
    return [self mqlread:query 
                    name:name
                delegate:delegate 
       didFinishSelector:finishedSelector
                 options:options];
}

- (id <FreebaseRequest>)mqlread:(id)query 
                name:(NSString*)name 
            delegate:(id) delegate 
   didFinishSelector:(SEL)finishedSelector
             options:(NSDictionary*)options
{
    
        
    FreebaseQuerier* querier = [[FreebaseQuerier alloc] initWithDelegate:delegate 
                                                            respSelector:finishedSelector
                                                               queryName:name 
                                                                 session:self];
    NSString *url = [FreebaseSession urlForMqlread:query name:name options:options];
    querier.isNamedResponse = YES;
    [querier requestUrl:url];
    //[querier release];
    return querier;
}

// writing
- (id <FreebaseRequest>)mqlwrite:(id)query 
                            name:(NSString*)name 
                        delegate: (id) delegate
               didFinishSelector:(SEL)finishedSelector
{
    if ([query isKindOfClass:[NSString class]])
        query = [query JSONValue];
    
    NSString* query_envelope = [[NSDictionary dictionaryWithObjectsAndKeys:
                                 query, @"query", nil] JSONRepresentation];
    
    
    FreebaseQuerier* querier = [[FreebaseQuerier alloc] initWithDelegate:delegate
                                                   respSelector:finishedSelector 
                                                      queryName:name 
                                                        session:self];
    [querier requestUrl:@"/api/service/mqlwrite" 
               postForm:[NSDictionary 
                         dictionaryWithObjectsAndKeys:query_envelope, @"query", nil]];
    querier.isNamedResponse = NO;
    
    return querier;
}


+ (NSString*)urlForMqlread:(id)query
{
    return [FreebaseSession urlForMqlread:query name:@"q1"];
}

+ (NSString*)urlForMqlread:(id)query name:(NSString*)name
{
    return [FreebaseSession urlForMqlread:query name:name 
                                  options:nil];
}

+ (NSString*)urlForMqlread:(id)query name:(NSString*)name options:(NSDictionary*)options
{
    // if they gave us a string, unpack it into an object, so we can wrap it
    if ([query isKindOfClass:[NSString class]]) {
        NSString* queryString = query;
        query = [queryString JSONValue];
    }
    
    if (!query) {
        NSLog(@"Error parsing query, aborting mqlread");
        return nil;
    }
    // wrap up in an envelope
    NSMutableDictionary *fullQuery;
    if (options)
        fullQuery = [NSMutableDictionary dictionaryWithDictionary:options];
    else
        fullQuery = [NSMutableDictionary dictionary];
    
    [fullQuery setObject:query forKey:@"query"];
    if (![fullQuery valueForKey:@"escape"])
        [fullQuery setObject:[NSNumber numberWithBool:NO] forKey:@"escape"];
    
    NSDictionary *envelope = [NSDictionary dictionaryWithObject:fullQuery forKey:name];
    
    // now make url args
    NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
                          envelope, @"queries",
                          @"0", @"indent",
                          nil];
    return [FreebaseQuerier relativeUrlForService:@"/api/service/mqlread" withArgs: args];
}

- (id <FreebaseRequest>)search:(id)query 
               name:(NSString*)name 
           delegate:(id) delegate
  didFinishSelector:(SEL) finishedSelector
{
    return [self search:query 
                   name:name 
               delegate:delegate 
      didFinishSelector:finishedSelector
                options:nil];
}

+ (NSString*) urlForSearch:(id)query
{
    return [FreebaseSession urlForSearch:query name:@"search1"];
}

+ (NSString*) urlForSearch:(id)query name:(NSString*)name
{
    return [FreebaseSession urlForSearch:query name:name options:nil];
}
+ (NSString*) urlForSearch:(id)query name:(NSString*)name options:(NSDictionary*)options
{
#pragma unused(name)
    NSMutableDictionary *args;
    if (options)
        args = [NSMutableDictionary dictionaryWithDictionary:options];
    else
        args = [NSMutableDictionary dictionary];
        
    if ([query isKindOfClass:[NSString class]])
        [args setObject:query forKey:@"query"];
    else if ([query isKindOfClass:[NSDictionary class]])
        [args addEntriesFromDictionary:query];
    // otherwise we assume it's a dictionary
    
    return [FreebaseQuerier relativeUrlForService:@"/api/service/search" withArgs:args];
}


- (id <FreebaseRequest>)search:(id)query 
               name:(NSString*)name 
           delegate:(id) delegate
  didFinishSelector:(SEL) finishedSelector
            options:(NSDictionary*)options
{
    FreebaseQuerier *querier = [[FreebaseQuerier alloc] initWithDelegate:delegate 
                                                            respSelector:finishedSelector 
                                                               queryName:name 
                                                                 session:self];
    querier.isNamedResponse = NO;
    querier.isJson = YES;
    NSString *url = [FreebaseSession urlForSearch:query name:name options:options];
    [querier requestUrl:url];
    //[querier release];
    return querier;
}

- (id <FreebaseRequest>)geosearch:(id)location
                             name:(NSString*)name
                         delegate:(id)delegate
                didFinishSelector:(SEL)finishedSelector
{
    return [self geosearch:location 
                      name:name 
                  delegate:delegate didFinishSelector:finishedSelector options:nil];
}

- (id <FreebaseRequest>)geosearch:(id)location
                             name:(NSString*)name 
                         delegate:(id) delegate
                didFinishSelector:(SEL) finishedSelector
                          options:(NSDictionary*)options
{
    // location is possibly a json object, json encode it if so
    if ([location isKindOfClass:[NSDictionary class]])
        location = [(NSString*)location JSONValue];

    // at the moment we force format=json and geometry_type=point
    NSMutableDictionary *args;
    if (options) {        
        args = [[NSMutableDictionary alloc] initWithDictionary:options];
        [args setObject:location forKey:@"location"];
        [args setObject:@"json" forKey:@"format"];
        [args setObject:@"point" forKey:@"geometry_type"];
    }
    else
        args = [[NSDictionary alloc] initWithObjectsAndKeys:
                location, @"location",
                @"json", @"format",
                @"point", @"geometry_type",
                nil];
    NSString* url = [FreebaseQuerier relativeUrlForService:@"/api/service/geosearch"
                                                  withArgs:args];
    [args release];
    FreebaseQuerier *querier = [[FreebaseQuerier alloc] initWithDelegate:delegate 
                                                            respSelector:finishedSelector 
                                                               queryName:name 
                                                                 session:self];
    
    [querier requestUrl:url];
    querier.isNamedResponse = NO;
    //querier.isResultResponse = NO;
    return querier;
}

- (id <FreebaseRequest>)touch
{
    return [self touch:@"touch" 
              delegate:nil
     didFinishSelector:@selector(nothing)];    
}

- (id <FreebaseRequest>)touch: (NSString*)name 
          delegate:(id)delegate
 didFinishSelector:(SEL)finishedSelector
{
    FreebaseQuerier *querier = [[FreebaseQuerier alloc] initWithDelegate:delegate
                                                                       respSelector:finishedSelector 
                                                                          queryName:name 
                                                                 session:self];
    querier.wantsRawData = YES;
    NSString *url = [FreebaseQuerier relativeUrlForService:@"/api/service/touch" withArgs:nil];
    [querier requestUrl:url];
    //[querier release];
    return querier;
}

- (id <FreebaseRequest>)blurb:(NSString*)content_id 
              name:(NSString*)name 
          delegate:(id)delegate
 didFinishSelector:(SEL)finishedSelector
{
    return [self blurb:content_id 
                  name:name 
              delegate:delegate 
     didFinishSelector:finishedSelector
               options:nil];
}

- (id <FreebaseRequest>)blurb:(NSString*)content_id 
              name:(NSString*)name 
          delegate:(id)delegate
 didFinishSelector:(SEL)finishedSelector
options:(NSDictionary *)options
{
    FreebaseQuerier *querier = [[FreebaseQuerier alloc] initWithDelegate:delegate 
                                                            respSelector:finishedSelector 
                                                               queryName:name 
                                                                 session:self];
    querier.isJson = NO;
    querier.isNamedResponse = NO;
    
    NSString *url = [FreebaseQuerier relativeUrlForService:[NSString stringWithFormat:@"/api/trans/blurb%@",
                                                            content_id] withArgs:options];
    [querier requestUrl:url];
    //[querier release];
    return querier;
}

- (NSString*)urlForImageThumb:(NSString*)image_id
                      options:(NSDictionary*)options
{
    if ([image_id isKindOfClass:[NSArray class]]) {
        if ([(NSArray*)image_id count] == 0)
            return nil;
        image_id = [(NSArray*)image_id objectAtIndex:0];
    }
    
    if ([image_id isKindOfClass:[NSDictionary class]]) {
        NSDictionary *image_dict = (NSDictionary*)image_id;
        image_id = [image_dict objectForKey:@"id"];
        if (!image_id)
            image_id = [image_dict objectForKey:@"guid"];
    }
    NSString *prefix = @"/api/trans/image_thumb%@";
    if (![image_id hasPrefix:@"/"])
        prefix = @"/api/trans/image_thumb/%@";
    
    image_id = [image_id stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    
    return [FreebaseQuerier relativeUrlForService:[NSString stringWithFormat:prefix,
                                                   image_id] withArgs:options];

}

- (id <FreebaseRequest>)imageThumb:(NSString*)image_id 
                              name:(NSString*)name 
                          delegate:(id)delegate
                 didFinishSelector:(SEL)finishedSelector
                           options:(NSDictionary*)options
{
    NSString *url = [self urlForImageThumb:image_id options:options];
        
    FreebaseQuerier *querier = 
    [[FreebaseQuerier alloc] initWithDelegate:delegate
                                 respSelector:finishedSelector 
                                    queryName:name 
                                      session:self];
    querier.wantsRawData = YES;
    
    [querier requestUrl:url cachePolicy:NSURLRequestUseProtocolCachePolicy];

    return querier;
}

- (id <FreebaseRequest>)uploadImage:(UIImage*)image
                        contentType:(NSString*)contentType
                              topic:(NSString*)topicId
                           delegate:(id)delegate
                  didFinishSelector:(SEL)finishedSelector
                            options:(NSDictionary*)options
{
    NSData* imageData;
    if ([contentType isEqualToString:@"image/jpeg"])
        // would be nice to just get this automatically from the image
        imageData = UIImageJPEGRepresentation(image, 0.75);
    else if ([contentType isEqualToString:@"image/png"])
        imageData = UIImagePNGRepresentation(image);
    else
        // invalid!
        return nil;
    
    NSMutableDictionary* args;
    if (options) {
        args = [NSMutableDictionary dictionaryWithDictionary:options];
    } else {
        args = [NSMutableDictionary dictionary];
    }
    [args setObject:contentType forKey:@"content_type"];
    
    NSString* url = [FreebaseQuerier relativeUrlForService:@"/api/service/upload"
                                                 withArgs:args];
    FreebaseQuerier *querier;
    
    if (topicId) {
        ImageAttacher* attacher = [[ImageAttacher alloc] initWithTopicId:topicId session:self];
        attacher.delegate = delegate;
        attacher.didFinishSelector = finishedSelector;
        
        querier = [[FreebaseQuerier alloc] initWithDelegate:attacher 
                                               respSelector:@selector(imageUploaded:) 
                                                  queryName:@"attacher" 
                                                    session:self];
    } else {
        querier = [[FreebaseQuerier alloc] initWithDelegate:delegate
                                               respSelector:finishedSelector 
                                                  queryName:@"upload" 
                                                    session:self];
    }
    
    querier.isNamedResponse = NO;
    
    [querier requestUrl:url postBody:imageData];
    
    return querier;
}


@end
